package com.starblink.android.charles

import android.os.Build
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.EncodeUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.SPStaticUtils
import com.blankj.utilcode.util.ScreenUtils
import com.google.gson.Gson
import com.google.gson.JsonParser
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import java.io.IOException
import java.util.Currency
import java.util.Locale
import java.util.TimeZone
import kotlin.collections.set


/**
 * 测试功能管理器
 * （1）数据抓包
 * （2）Mock数据
 */
object CharlesManager {
    const val TAG = "CharlesManager"

    //当前环境 pub test
    private var getCurrSvrCfgEnvName:(()->String)?=null
    //当前国家 UK
    private var getLocalCountry:(()->String)?=null
    //设备id
    var deviceId:String?=null
    //是否生产环境 生产环境不进行抓包
    var isPub:(()->Boolean)?=null
    //获取编译时间
    private var buildTime:String?=null

    private var appScene = "Guang"

    /** 设置抓包平替参数 **/
    fun initHeaderParams(deviceId:String, appScene: String = "Guang", getCurrSvrCfgEnvName:(()->String)?=null,getLocalCountry:(()->String)?=null
                         ,isPub:(()->Boolean)?=null,buildTime:String?=null){
        this.deviceId=deviceId
        this.appScene = appScene
        this.getCurrSvrCfgEnvName=getCurrSvrCfgEnvName
        this.getLocalCountry=getLocalCountry
        this.isPub=isPub
        this.buildTime=buildTime
    }

    /**true，上传包信息*/
    fun isUploadPacketInfo(): Boolean {
        return SPStaticUtils.getBoolean(CharlesConfig.UploadPacketInfo_KEY, false)
    }

    fun setUploadPacketInfo(animCheckStatus: Boolean) {
        SPStaticUtils.put(CharlesConfig.UploadPacketInfo_KEY, animCheckStatus)
    }

    /**true，Mock包信息*/
    fun isMockPacketInfo(): Boolean {
        return SPStaticUtils.getBoolean(CharlesConfig.MockPacketInfo_KEY, false)
    }

    fun setMockPacketInfo(animCheckStatus: Boolean) {
        SPStaticUtils.put(CharlesConfig.MockPacketInfo_KEY, animCheckStatus)
    }

    private val client = OkHttpClient.Builder()
        .addInterceptor(
            HttpLoggingInterceptor()
                .apply {
                    setLevel(HttpLoggingInterceptor.Level.BODY)
                }).build()

    /**
     * 将接口请求和响应相传至服务器
     */
//    fun <D : Operation.Data> uploadApiResponse(
//        request: ApolloRequest<D>,
//        response: ApolloResponse<D>,
//    ) {
//        CoroutineScope(Dispatchers.IO).launch {
//            // 设置请求参数
//            val params = mutableMapOf<String, Any>(
//                "business_sence" to "Guang",
//                "request_contents_raw" to mapOf(
//                    "operationName" to "${request.operation.name()}",
//                    "request_contents_raw" to request.operation,
//                ),
//                "response_contents_raw" to mapOf(
//                    "operationName" to "${request.operation.name()}",
//                    "response_contents_raw" to response.data
//                ),
//            )
//            request.httpHeaders?.let {
//                val headers = mutableMapOf<String, String>()
//                it.forEach { headers[it.name] = it.value }
//                params["request_headers_raw"] = mapOf(
//                    "operationName" to "${request.operation.name()}",
//                    "request_headers_raw" to headers
//                )
//            }
//            response.executionContext[HttpInfo]?.let {
//                val headers = mutableMapOf<String, String>()
//                it.headers.forEach { headers[it.name] = it.value }
//                params["response_headers_raw"] = mapOf(
//                    "operationName" to "${request.operation.name()}",
//                    "response_headers_raw" to headers
//                )
//                params["response_during"] = it.endMillis - it.startMillis
//            }
//            val requestBody = params.toJson().toRequestBody("application/json".toMediaTypeOrNull())
//
//            val call = client.newCall(
//                Request.Builder()
////                    .url("http://10.118.56.197:8080/app/uploadresponse/insert")
//                    .url("http://172.16.83.215:8080/app/uploadresponse/insert")
//                    .headers(headerBuilder())
//                    .post(requestBody).build()
//            )
//            try {
//                if (call.execute().isSuccessful) {
//                    // 上传成功
//                    LogUtils.e(TAG, "上传成功123456")
//                } else {
//                    // 上传失败
//                    LogUtils.e(TAG, "上传失败654321")
//                }
//            } catch (e: IOException) {
//                e.printStackTrace()
//                LogUtils.e(TAG, e.toString())
//            } catch (e: Exception) {
//                e.printStackTrace()
//                LogUtils.e(TAG, e.toString())
//            }
//        }
//    }

    /**
     * 将接口请求和响应相传至服务器
     */
    fun uploadApiResponse(
        request: Request,
        response: Response,
        tookMs: Long,
    ) {
        // 设置请求参数
        val params = mutableMapOf<String, Any>(
            "business_sence" to appScene,
            "url" to request.url.toString()
        )

        // 参数：request_headers_raw
        val requestHeaders = mutableMapOf<String, String>()
        request.headers.forEach {
            requestHeaders[it.first] = it.second
        }
        params["request_headers_raw"] = requestHeaders

        if (request.body != null) {
            request.body!!.run {
                val buffer1 = Buffer()
                writeTo(buffer1)
                val content1 = buffer1.readString(Charsets.UTF_8)
                params["request_contents_raw"] = JsonParser.parseString(content1)
            }
        } else {
            params["request_contents_raw"] = JsonParser.parseString("")
        }

        val responseHeaders = mutableMapOf<String, String>()
        response.headers.forEach {
            responseHeaders[it.first] = it.second
        }
        params["response_headers_raw"] = responseHeaders

        val content2 = response.peekBody(Long.MAX_VALUE).string()
        params["response_contents_raw"] = JsonParser.parseString(content2)

        params["response_during"] = tookMs

        val realRequestBody =
            params.toJson().toRequestBody("application/json".toMediaTypeOrNull())
        val call = client.newCall(
            Request.Builder()
//                    .url("http://10.118.58.145:8080/app/uploadresponse/insert")
                .url(CharlesConfig.uploadLog)
                .headers(headerBuilder())
                .post(realRequestBody).build()
        )
        try {
            call.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    // 上传失败
                    LogUtils.e(TAG, "上传失败654321")
                }

                override fun onResponse(call: Call, response: Response) {
                    // 上传成功
                    LogUtils.e(TAG, "上传成功123456")
                }
            })
        } catch (e: IOException) {
            e.printStackTrace()
            LogUtils.e(TAG, e.toString())
        } catch (e: Exception) {
            e.printStackTrace()
            LogUtils.e(TAG, e.toString())
        }
    }

    //绑定二维码
    fun reqBindCatch(loadUrl: String, success: () -> Unit, fail: () -> Unit) {
        var reqUrl=loadUrl
        if (!reqUrl.contains("deviceId=")) {
            //开启抓包平替功能
            reqUrl += if (!reqUrl.contains("?")) {
                "?deviceId=$deviceId"
            } else {
                "&deviceId=$deviceId"
            }
        }
        CoroutineScope(Dispatchers.IO).launch {
            val call = client.newCall(
                Request.Builder()
                    .url(reqUrl)
                    .headers(headerBuilder())
                    .get().build()
            )
            try {
                if (call.execute().isSuccessful) {
                    LogUtils.e(TAG, "reqBindCatch Successful")
                    success.invoke()
                } else {
                    // 上传失败
                    LogUtils.e(TAG, "reqBindCatch error")
                    fail.invoke()
                }
            } catch (e: IOException) {
                e.printStackTrace()
                LogUtils.e(TAG, e.toString())
                fail.invoke()
            } catch (e: Exception) {
                e.printStackTrace()
                LogUtils.e(TAG, e.toString())
                fail.invoke()
            }
        }
    }
    //取消mock
    fun reqCancelMock(success: (() -> Unit)?=null, fail: (() -> Unit)?=null) {
        CoroutineScope(Dispatchers.IO).launch {
            val call = client.newCall(
                Request.Builder()
                    .url("${CharlesConfig.cancelMockLog}?deviceId=$deviceId")
                    .headers(headerBuilder())
                    .get().build()
            )
            try {
                if (call.execute().isSuccessful) {
                    LogUtils.e(TAG, "reqCancelMockLog Successful")
                    success?.invoke()
                } else {
                    LogUtils.e(TAG, "reqCancelMockLog error")
                    fail?.invoke()
                }
            } catch (e: IOException) {
                e.printStackTrace()
                LogUtils.e(TAG, e.toString())
                fail?.invoke()
            } catch (e: Exception) {
                e.printStackTrace()
                LogUtils.e(TAG, e.toString())
                fail?.invoke()
            }
        }
    }

    private fun headerBuilder(): Headers {
        return Headers.Builder()
            .add("app_env", getCurrSvrCfgEnvName?.invoke()?: "")
            .add("app_version", AppUtils.getAppVersionName())
            .add("app_versioncode", AppUtils.getAppVersionCode().toString())
            .add("app_lan", Locale.getDefault().language)
            .add(
                "app_currency",
                EncodeUtils.urlEncode(Currency.getInstance(Locale.getDefault()).getSymbol())
            )
            .add("app_country", getLocalCountry?.invoke()?: "")
            .add("platform", "and")
            .add("device_id", deviceId?: "")
            .add("device_type", "${Build.BRAND} ${Build.MODEL}")
            .add("brand", "${Build.BRAND}")
            .add(
                "device_screen",
                "${ScreenUtils.getScreenWidth()}*${ScreenUtils.getScreenHeight()}"
            )
            .add("device_os", Build.VERSION.RELEASE)
            .add("device_ip", NetworkUtils.getIPAddress(true))
            .add("build_number", buildTime?:"")
            .add("tz", TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT))
            .build()
    }

    private fun Any.toJson() = Gson().toJson(this)
}